package com.hmall.item.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmall.api.dto.OrderDetailDTO;
import com.hmall.common.domain.PageDTO;
import com.hmall.common.exception.BizIllegalException;
import com.hmall.common.utils.BeanUtils;
import com.hmall.item.domain.dto.ItemDTO;
import com.hmall.item.domain.po.Item;
import com.hmall.item.domain.query.ItemPageQuery;
import com.hmall.item.mapper.ItemMapper;
import com.hmall.item.service.IItemService;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 商品表 服务实现类
 * </p>
 *
 * @author itheima
 */
@Service
public class ItemServiceImpl extends ServiceImpl<ItemMapper, Item> implements IItemService {

    @Autowired
    private RestHighLevelClient client;

    @Override
    public void deductStock(List<OrderDetailDTO> items) {
        String sqlStatement = "com.hmall.item.mapper.ItemMapper.updateStock";
        boolean r = false;
        try {
            r = executeBatch(items, (sqlSession, entity) -> sqlSession.update(sqlStatement, entity));
        } catch (Exception e) {
//            log.error("更新库存异常", e);
//            return;
            throw new BizIllegalException("库存不足！");
        }
        if (!r) {
            throw new BizIllegalException("库存不足！");
        }
    }

    @Override
    public List<ItemDTO> queryItemByIds(Collection<Long> ids) {
        return BeanUtils.copyList(listByIds(ids), ItemDTO.class);
    }

    @Override
    public PageDTO<ItemDTO> search(ItemPageQuery itemPageQuery) {
        //1. 构建查询请求
        SearchRequest request = new SearchRequest("items");

        //2. 设置查询条件
        //2-1 设置复合查询
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //2-2 搜索框
        String key = itemPageQuery.getKey();
        if (StrUtil.isEmpty(key)) {
            //查询所有
            boolQuery.must(QueryBuilders.matchAllQuery());
        } else {
            //在名称和品牌字段中查询指定关键字
            boolQuery.must(QueryBuilders.multiMatchQuery(key, "name", "brand"));
        }

        //2-3 过滤栏
        //分类
        if (StrUtil.isNotEmpty(itemPageQuery.getCategory())) {
            boolQuery.filter(QueryBuilders.termQuery("category", itemPageQuery.getCategory()));
        }
        //品牌
        if (StrUtil.isNotEmpty(itemPageQuery.getBrand())) {
            boolQuery.filter(QueryBuilders.termQuery("brand", itemPageQuery.getBrand()));
        }
        //价格
        if (itemPageQuery.getMinPrice() != null && itemPageQuery.getMaxPrice() != null) {
            boolQuery.filter(QueryBuilders.rangeQuery("price")
                    .lte(itemPageQuery.getMaxPrice()).gte(itemPageQuery.getMinPrice()));
        }

        // TODO: 竞价排名
        //2-4 使用算分函数 提供广告商品的排名
        FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders.functionScoreQuery(boolQuery,
                new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                QueryBuilders.termQuery("isAD", true),
                                ScoreFunctionBuilders.weightFactorFunction(50))
                }
        );

        request.source().query(functionScoreQuery);

        //3. 设置分页
        request.source()
                .from((itemPageQuery.getPageNo() - 1) * itemPageQuery.getPageSize())
                .size(itemPageQuery.getPageSize());

        //4. 设置排序
        if (StrUtil.isNotEmpty(itemPageQuery.getSortBy())) {
            request.source().sort(itemPageQuery.getSortBy(), itemPageQuery.getIsAsc() ? SortOrder.ASC : SortOrder.DESC);
        }

        //5. 设置高亮
        request.source().highlighter(
                new HighlightBuilder().field("name").preTags("<em>").postTags("</em>").requireFieldMatch(false)
        );

        try {

            //6. 发送请求
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);

            //7. 处理返回结果
            //总记录数
            long total = response.getHits().getTotalHits().value;
            //总页数
            long pages = total % itemPageQuery.getPageSize() == 0 ? (total / itemPageQuery.getPageSize()) : (total / itemPageQuery.getPageSize() + 1);
            //当期页数据
            List<ItemDTO> itemDTOList =
                    Arrays.asList(response.getHits().getHits()).stream().map(e -> {
                                //获取文档内容
                                ItemDTO itemDTO = JSONUtil.toBean(e.getSourceAsString(), ItemDTO.class);
                                //处理高亮
                                Map<String, HighlightField> highlightFields = e.getHighlightFields();
                                if (CollUtil.isNotEmpty(highlightFields)){
                                    HighlightField highlightField = highlightFields.get("name");
                                    if (highlightField !=null){
                                        itemDTO.setName(highlightField.getFragments()[0].string());
                                    }
                                }
                                return itemDTO;
                            }
                    ).collect(Collectors.toList());

            return new PageDTO<>(total, pages, itemDTOList);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
